#include "testing/testing.hpp"

#include "tbcore/pch.hpp"

#include "archive.hpp"
#include "tbcore/base/global_initializer.hpp"
#include "tbcore/base/file.hpp"

TB_NAMESPACE_BEGIN

struct CompositionType;
typedef shared_ptr<struct CompositionType>  CompositionTypePtr;

struct CompositionType : public Object {
	DECLARE_reflection(CompositionType, Object);
	CompositionType() {
	
	}

	CompositionType(const CompositionType& rhs) {
		bval = rhs.bval;
		u8val = rhs.u8val;
		i8val = rhs.i8val;
		u16val = rhs.u16val;
		i16val = rhs.i16val;
		u32val = rhs.u32val;
		i32val = rhs.i32val;
		u64val = rhs.u64val;
		i64val = rhs.i64val;
		strval = rhs.strval;
		wstrval = rhs.wstrval;
		oidval = rhs.oidval;
		dtval = rhs.dtval;
		duval = rhs.duval;
		prval = rhs.prval;
		dvecval = rhs.dvecval;
		memcpy(arrval, rhs.arrval, sizeof(double) * 10);
	}

	void init() {
		bval = false;
		u8val = 12;
		i8val = 13;
		u16val = 14;
		i16val = 15;
		u32val = 16;
		i32val = 17;
		u64val = 18;
		i64val = 19;
		strval = "tizzybec";
		wstrval = _T("widestring");
		oidval = UniqueId::GenerateUID();
		dtval = DateTime(12345);;
		duval = Duration(54321);
		prval.first = 11;
		prval.second = 12.12334514234345345;
		dvecval.resize(10, 10.54);
		for (int i = 0; i < 10; ++i) {
			arrval[i] = 10.10;
		}
	}

	bool bval;
	uint8 u8val;
	int8 i8val;
	uint16 u16val;
	int16 i16val;
	uint32 u32val;
	int32 i32val;
	uint64 u64val;
	int64 i64val;
	_STD string strval;
	string16 wstrval;
	UniqueId oidval;
	DateTime dtval;
	Duration duval;
	std::pair<int, double> prval;
	std::vector<double> dvecval;
	double arrval[10];
};

REGISTER_BEGIN(CompositionType)
REGISTER_FIELD(bval, "bval");
REGISTER_FIELD(i8val, "i8val");
REGISTER_FIELD(u8val, "u8val");
REGISTER_FIELD(i16val, "i16val");
REGISTER_FIELD(u16val, "u16val");
REGISTER_FIELD(i32val, "i32val");
REGISTER_FIELD(u32val, "u32val");
REGISTER_FIELD(i64val, "i64val");
REGISTER_FIELD(u64val, "u64val");
REGISTER_FIELD(strval, "strval");
REGISTER_FIELD(wstrval, "wstrval");
REGISTER_FIELD(oidval, "oidval");
REGISTER_FIELD(dtval, "dtval");
REGISTER_FIELD(duval, "duval");
REGISTER_FIELD(prval, "prval");
REGISTER_FIELD(dvecval, "dvecval");
REGISTER_FIELD(arrval, "arrval")
REGISTER_END()

REGISTER_PAIR(int, double);
REGISTER_STD_VECTOR(double);
REGISTER_STD_VECTOR(CompositionType);
REGISTER_STD_VECTOR(CompositionTypePtr);
REGISTER_TRIVAL_ARRAY(double, 10);

class MashalTest : public ::testing::Test {
protected:
	virtual void SetUp() {
		bval = false;
		u8val = 12;
		i8val = 13;
		u16val = 14;
		i16val = 15;
		u32val = 16;
		i32val = 17;
		u64val = 18;
		i64val = 19;
		strval = "tizzybec";
		wstrval = _T("widestring");
		oidval = UniqueId::GenerateUID();
		dtval = DateTime(12345);;
		duval = Duration(54321);
		com.init();
		comptr = make_shared<CompositionType>();
		comptr->init();
		prval.first = 11;
		prval.second = 12.12334514234345345233454;
		dvecval.resize(10, 10.54);
		comvecval.resize(10);
		for (int i = 0; i < 10; ++i) {
			comvecval[i].init();
			comptrvecval.push_back(make_shared<CompositionType>());
			comptrvecval.back()->init();
			arrval[i] = 10.10;
		}
	}

	bool bval;
	uint8 u8val;
	int8 i8val;
	uint16 u16val;
	int16 i16val;
	uint32 u32val;
	int32 i32val;
	uint64 u64val;
	int64 i64val;
	_STD string strval;
	string16 wstrval;
	UniqueId oidval;
	DateTime dtval;
	Duration duval;
	std::pair<int, double> prval;
	std::vector<double> dvecval;
	double arrval[10];
	CompositionType com;
	CompositionTypePtr comptr;
	std::vector<CompositionType> comvecval;
	std::vector<CompositionTypePtr> comptrvecval;
};

inline bool operator==(const CompositionType& lhs, const CompositionType& rhs) {
	return lhs.bval == rhs.bval &&
		lhs.u8val == rhs.u8val &&
		lhs.i8val == rhs.i8val &&
		lhs.u16val == rhs.u16val &&
		lhs.i16val == rhs.i16val &&
		lhs.u32val == rhs.u32val &&
		lhs.i32val == rhs.i32val &&
		lhs.u64val == rhs.u64val &&
		lhs.i64val == rhs.i64val &&
		lhs.strval == rhs.strval &&
		lhs.wstrval == rhs.wstrval &&
		lhs.oidval == rhs.oidval &&
		lhs.dtval == rhs.dtval &&
		lhs.duval == rhs.duval &&
		lhs.prval == rhs.prval &&
		lhs.dvecval == rhs.dvecval;
}

TEST_F(MashalTest, JsonProtocol) {
	MemoryBuffer buff;

	{
		SerVariantAsJson(Variant::FromValue(bval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<bool>(), bval);
	}
	
	{
		SerVariantAsJson(Variant::FromValue(u8val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint8>(), u8val);
	}

	{
		SerVariantAsJson(Variant::FromValue(i8val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ((v.Value<int8>()), i8val);
	}

	{
		SerVariantAsJson(Variant::FromValue(u16val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint16>(), u16val);
	}

	{
		SerVariantAsJson(Variant::FromValue(i16val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<int16>(), i16val);
	}

	{
		SerVariantAsJson(Variant::FromValue(u32val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint32>(), u32val);
	}

	{
		SerVariantAsJson(Variant::FromValue(i32val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<int32>(), i32val);
	}

	{
		SerVariantAsJson(Variant::FromValue(u64val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint64>(), u64val);
	}

	{
		SerVariantAsJson(Variant::FromValue(i64val), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<int64>(), i64val);
	}

	{
		SerVariantAsJson(Variant::FromValue(strval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<_STD string>(), strval);
	}

	{
		SerVariantAsJson(Variant::FromValue(wstrval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<string16>(), wstrval);
	}

	{
		SerVariantAsJson(Variant::FromValue(oidval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<UniqueId>(), oidval);
	}

	{
		SerVariantAsJson(Variant::FromValue(dtval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<DateTime>(), dtval);
	}

	{
		SerVariantAsJson(Variant::FromValue(duval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<Duration>(), duval);
	}

	{
		SerVariantAsJson(Variant::FromValue(prval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ((v.Value<std::pair<int, double> >()), prval);
	}

	{
		SerVariantAsJson(Variant::FromValue(dvecval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<std::vector<double>  >(), dvecval);
	}

	{
		SerVariantAsJson(Variant::FromValue(com), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		CompositionType com1 = v.Value<CompositionType>();
		EXPECT_TRUE((com1 == com));
	}

	{
		SerVariantAsJson(Variant::FromValue(comptr), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		CompositionTypePtr dv = v.Value<CompositionTypePtr>();
		EXPECT_EQ(*dv, *comptr);
	}

	{
		SerVariantAsJson(Variant::FromValue(comvecval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<std::vector<CompositionType>  >(), comvecval);
	}

	{
		SerVariantAsJson(Variant::FromValue(comptrvecval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		std::vector<CompositionTypePtr> dv = v.Value<std::vector<CompositionTypePtr> >();
		EXPECT_EQ(comptrvecval.size(), dv.size());
		if (comptrvecval.size() == dv.size()) {
			for (uint32 i = 0; i < dv.size(); ++i) {
				EXPECT_EQ(*comptrvecval[i], *dv[i]) << i;
			}
		}
	}

	{
		SerVariantAsJson(Variant::FromValue(arrval), buff);
		Variant v;
		DeserJsonAsVariant(buff, v);
		EXPECT_EQ(v.Value<std::vector<double>  >(), std::vector<double>(10, 10.10));
	}
}

TEST_F(MashalTest, MsgpackProtocol) {
	MemoryBuffer buff;

	{
		SerVariantAsBinary(Variant::FromValue(bval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<bool>(), bval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(u8val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint8>(), u8val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(i8val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ((v.Value<int8>()), i8val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(u16val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint16>(), u16val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(i16val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<int16>(), i16val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(u32val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint32>(), u32val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(i32val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<int32>(), i32val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(u64val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<uint64>(), u64val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(i64val), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<int64>(), i64val);
	}

	{
		SerVariantAsBinary(Variant::FromValue(strval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<_STD string>(), strval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(wstrval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<string16>(), wstrval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(oidval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<UniqueId>(), oidval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(dtval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<DateTime>(), dtval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(duval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<Duration>(), duval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(prval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v); 
		EXPECT_TRUE((v.Value<std::pair<int, double> >()) == prval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(dvecval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<std::vector<double>  >(), dvecval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(com), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		CompositionType com1 = v.Value<CompositionType>();
		EXPECT_TRUE((com1 == com));
	}

	{
		SerVariantAsBinary(Variant::FromValue(comptr), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		CompositionTypePtr dv = v.Value<CompositionTypePtr>();
		EXPECT_EQ(*dv, *comptr);
	}

	{
		SerVariantAsBinary(Variant::FromValue(comvecval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		Variant* v1 = *v.GetArray();
		std::vector<CompositionType> dv = v.Value<std::vector<CompositionType> >();
		EXPECT_EQ(dv, comvecval);
	}

	{
		SerVariantAsBinary(Variant::FromValue(comptrvecval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		std::vector<CompositionTypePtr> dv = v.Value<std::vector<CompositionTypePtr> >();
		EXPECT_EQ(comptrvecval.size(), dv.size());
		if (comptrvecval.size() == dv.size()) {
			for (uint32 i = 0; i < dv.size(); ++i) {
				EXPECT_EQ(*comptrvecval[i], *dv[i]) << i;
			}
		}
	}

	{
		SerVariantAsBinary(Variant::FromValue(arrval), buff);
		Variant v;
		DeserBinaryAsVariant(buff, v);
		EXPECT_EQ(v.Value<std::vector<double>  >(), std::vector<double>(10, 10.10));
	}
}

TB_NAMESPACE_END